#pragma once
#include "../server/tcpServer.hpp"
#include <sys/stat.h>

class Util
{
public:
    static size_t Split(const std::string& src, const std::string& sep, std::vector<std::string>& arry, bool keep_blank = false)
    {
        size_t offset = 0;
        while(true)
        {
            size_t pos = src.find(sep, offset);
            if(pos == std::string::npos) //后续字符串中不存在sep分割节点
            {
                if(offset != src.size() || keep_blank == true) //查找位置后存在有效内容或不存在有效内容(字符串末尾为sep分割节点)但需要保留空串
                    arry.push_back(src.substr(offset));
                break;
            }
            else
            {
                
                if(pos != offset || keep_blank == true) //查找位置与sep分割节点之间存在字符或是查找位置后即是sep分割节点(连续的分割节点)但需要保留空串
                    arry.push_back(src.substr(offset, pos - offset));
                offset = pos + sep.size();
            }
        }
        return arry.size();
    }

    static bool ReadFile(const std::string& filename, std::string* content)
    {
        std::ifstream ifs(filename, std::ios::binary);
        if(ifs.is_open() == false)
        {
            logMessage(ERROR, "open %s file failed, %d:%s", filename.c_str(), errno, strerror(errno));
            return false;
        }

        ifs.seekg(0, ifs.end); //将提取位置置于文件末尾，以获取文件大小
        size_t fsize = ifs.tellg();
        ifs.seekg(0, ifs.beg);//将提取位置置于文件起始，以读取

        content->resize(fsize);
        ifs.read(&((*content)[0]), fsize);
        if(ifs.good() == false)
        {
            logMessage(ERROR, "read %s file failed, %d:%s", filename.c_str(), errno, strerror(errno));
            return false;
        }

        ifs.close();
        return true;
    }

    static bool WriteFile(const std::string& filename, const std::string& content)
    {   
        std::ofstream ofs(filename, std::ios::binary | std::ios::trunc);
        if(ofs.is_open() == false)
        {
            logMessage(ERROR, "open %s file failed, %d:%s", filename.c_str(), errno, strerror(errno));
            return false;
        }

        ofs.write(content.c_str(), content.size());
        if(ofs.good() == false)
        {
            logMessage(ERROR, "write %s file failed, %d:%s", filename.c_str(), errno, strerror(errno));
            return false;
        }
        ofs.close();
        return true;
    }

    //url的转码
    static std::string UrlEncode(const std::string& url, bool convert_sapce_to_plus = false)
    {
        std::string res;
        for(const char& c : url)
        {
            if(c == '.' || c == '-' || c == '_' || c == '~' || isalnum(c)) //符号中的 . - _ ~ 不需要转码，十进制数字与字母不需要转码
            {
                res += c;
            }
            else if(c == ' ' && convert_sapce_to_plus == true) //W3C/HTML编码需要将空格编码为'+'
            {
                res += '+';
            }
            else //剩余字符转为'%' + ascii码值的形式，总共占3个字节(char类型占1字节，需要用两位十六进制来保存)
            {
                char tmp[4] = {0};
                snprintf(tmp, 4, "%%%02X", c); 
                res += tmp; 
            }
        }
        return res;
    }

    //16进制转10进制
    static char HexToDec(char hex) 
    {
        if(hex >= '0' && hex <= '9')
            return hex - '0';
        else if(hex >= 'A' && hex <= 'Z')
            return 10 + hex - 'A';
        else if(hex >= 'a' && hex <= 'z')
            return 10 + hex - 'a';
        else
            return  -1;
    }

    //url的解码
    static std::string UrlDecode(const std::string& url)
    {
        int len = url.size(), cur = 0;
        std::string res;
        while(cur < len)
        {
            if(url[cur] == '%' && cur + 2 < len) //'%'表示后面两字节保存的是一个字符所转成的两位十六进制数，需要将其解码为字符
            {
                char c = (HexToDec(url[cur+1]) << 4) + HexToDec(url[cur+2]);
                res += c;
                cur += 3;
            }
            else if(url[cur] == '+') //除了W3C/HTML编码需要将空格编码为'+'，编码后不会存在其他'+'，因此'+'必定解码为空格
            {
                res += ' ';
                cur++;
            }
            else //剩余情况解码不需要改变
            {
                res += url[cur];
                cur++; 
            }
        }
        return res;
    }

    //获取http状态码对应的含义
    static std::string StatuDesc(int statu)
    {
        if(status_msg.find(statu) == status_msg.end())
            return "Unknow statu";
        return status_msg[statu];
    }
    
    //获取文件对应的mime类型
    static std::string ExtMime(const std::string& filename)
    {
        size_t pos = filename.rfind('.');
        if(pos == std::string::npos)
            return "application/octet-stream"; // 目录文件，返回二进制流
            
        std::string suffix = filename.substr(pos);
        auto it = _mime_msg.find(filename.substr(pos));
        if( it == _mime_msg.end())
            return "application/octet-stream"; // 不存在对应的mime类型，返回二进制流
        else
            return it->second;
    }

    //是否为目录
    static bool IsDirectory(const std::string& filename)
    {
        struct stat st;
        int ret = stat(filename.c_str(), &st);
        if(ret < 0)
            return false;
        return S_ISDIR(st.st_mode); 
    }

    //是否为普通文件
    static bool IsRegular(const std::string& filename)
    {
        struct stat st;
        int ret = stat(filename.c_str(), &st);
        if(ret < 0)
            return false;
        return S_ISREG(st.st_mode); 
    }

    //判断http请求的资源路径(相对路径)是否有效，客户端只能请求限制目录内(工作路径 + 限制路径)中的资源
    //计算目录深度，当目录深度小于0则表示当前路径离开了限制目录，若最后没有回到限制目录内则无效
    static bool VaildPath(const std::string& path, const std::string& limit_path = "")
    {
        std::vector<std::string> subdir;
        Split(path, "/", subdir); //'/'之间的空串不做任何的路径转变，因此不需要保留

        std::string work_path = getcwd(NULL, 0); //获取工作目录的绝对路径
        work_path += "/" + limit_path; //获取限制目录的绝对路径
        std::vector<std::string> limit_dir; 
        Split(work_path, "/", limit_dir); 
        int pos = limit_dir.size(); //当深度小于0时想要返回限制目录所需的跳转位置
        int ture_step = 0; //成功跳转回限制目录所需的次数
        int level = 0;
        for(std::string& dir : subdir)
        {
            if(dir == "..") //路径上移，深度减一
            {
                if((level - 1) < 0) //深度减一后深度小于0，表示跳转到了限制目录的外层目录
                {
                    if(ture_step == limit_dir.size() - pos) ture_step++; //当在限制路径中路径上移才执行，否则(在偏离限制路径时的路径上移，返回限制目录的步骤不变)不执行
                    pos--;
                }
                level--;
            }
            else if(dir == ".") //前往当前目录，深度不变
            {
                continue;
            }
            else //路径下移，深度加一
            {
                if(level < 0) //原本深度小于零，表示已处于限制目录的外层目录
                {
                    if(ture_step == limit_dir.size() - pos && dir == limit_dir[pos]) //当前处于限制路径(没有偏离)且跳转到正确的目录
                        ture_step--;
                    pos++;
                }
                level++;
            }
        }

        if(level < 0 || ture_step > 0) //当前位置处于限制目录的上层或是没有回到限制目录中，则该路径无效
            return false;
        return true;
    }

private:
    static std::unordered_map<int, std::string> status_msg; 
    static std::unordered_map<std::string, std::string> _mime_msg;
};

std::unordered_map<int, std::string> Util::status_msg = {
    {100, "Continue"}, {101, "Switching Protocol"}, {102, "Processing"}, {103, "Early Hints"},

    {200, "OK"}, {201, "Created"}, {202, "Accepted"}, {203, "Non-Authoritative Information"}, {204, "No Content"}, 
    {205, "Reset Content"}, {206, "Partial Content"}, {207, "Multi-Status"}, {208, "Already Reported"}, 
    {226, "IM Used"}, 

    {300, "Multiple Choices"}, {301, "Moved Permanently"}, {302, "Found"}, {303, "See Other"}, {304, "Not Modified"}, 
    {307, "Temporary Redirect"}, {308, "Permanent Redirect"}, 

    {400, "Bad Request"}, {401, "Unauthorized"}, {402, "Payment Required"}, {403, "Forbidden"}, {404, "Not Found"}, 
    {405, "Method Not Allowed"}, {406, "Not Acceptable"}, {407, "Proxy Authentication Required"}, {408, "Request Timeout"}, {409, "Conflict"}, 
    {410, "Gone"}, {411, "Length Required"}, {412, "Precondition Failed"}, {413, "Content Too Large"}, {414, "URI Too Long"}, 
    {415, "Unsupported Media Type"}, {416, "Range Not Satisfiable"}, {417, "Expectation Failed"}, {418, "I'm a teapot"}, 
    {421, "Misdirected Request"}, {422, "Unprocessable Entity"}, {423, "Locked"}, {424, "Failed Dependency"}, 
    {425, "Too Early"}, {426, "Upgrade Required"}, {428, "Precondition Required"}, {429, "Too Many Requests"}, 
    {431, "Request Header Fields Too Large"}, {451, "Unavailable For Legal Reasons"}, 

    {500, "Internal Server Error"}, {501, "Not Implemented"}, {502, "Bad Gateway"}, {503, "Service Unavailable"}, {504, "Gateway Timeout"}, 
    {505, "HTTP Version Not Supported"}, {506, "Variant Also Negotiates"}, {507, "Insufficient Storage"}, {508, "Loop Detected"},
    {510, "Not Extended"}, {511, "Network Authentication Required"}
};

std::unordered_map<std::string, std::string> Util::_mime_msg = {
    {".aac", "audio/aac"}, {".abw", "application/x-abiword"}, {".apng", "image/apng"}, {".arc", "application/x-freearc"}, 
    {".avif", "image/avif"}, {".avi", "video/x-msvideo"}, {".azw", ".bin"}, 
    
    {".bmp", "image/bmp"}, {".bz", "application/x-bzip"}, {".bz2", "application/x-bzip2"}, 

    {".cda", "application/x-cdf"}, {".csh", "application/x-csh"}, {".css", "text/css"}, {".csv", "text/csv"},

    {".doc", "application/msword"}, {".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"}, 

    {".eot", "application/vnd.ms-fontobject"}, {".epub", "application/epub+zip"}, 
    
    {".gz", "application/gzip"}, {".gif", "image/gif"},

    {".htm", "text/html"}, {".html", "text/html"}, 

    {".ico", "image/vnd.microsoft.icon"}, {".ics", "text/calendar"}, 
    
    {".jar", "application/java-archive"}, {".jpeg", "image/jpeg"}, {".jpg", "image/jpeg"}, {".js", "text/javascript"}, 
    {".json", "application/json"}, {".jsonld", "application/ld+json"}, 

    {".mid", "audio/midi"}, {".midi", "audio/x-midi"}, {".mjs", "text/javascript"}, {".mp3", "audio/mpeg"}, 
    {".mp4", "video/mp4"}, {".mpeg", "video/mpeg"}, {".mpkg", "application/vnd.apple.installer+xml"}, 
    
    {".odp", "application/vnd.oasis.opendocument.presentation"}, {".ods", "application/vnd.oasis.opendocument.spreadsheet"}, 
    {".odt", "application/vnd.oasis.opendocument.text"}, {".oga", "audio/ogg"}, {".ogv", "video/ogg"}, {".ogx", "application/ogg"}, 
    {".opus", "audio/opus"}, {".otf", "font/otf"}, 

    {".png", "image/png"}, {".pdf", "application/pdf"}, {".php", "application/x-httpd-php"}, {".ppt", "application/vnd.ms-powerpoint"}, 
    {".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"}, 

    {".rar", "application/vnd.rar"}, {".rtf", "application/rtf"}, 	
    
    {".sh", "application/x-sh"}, {".svg", "image/svg+xml"}, 
    
    {".tar", "application/x-tar"}, {".tif", "image/tiff"}, {".tiff", "image/tiff"}, {".ts", "video/mp2t"}, {".ttf", "font/ttf"}, {".txt", "text/plain"},

    {".vsd", "application/vnd.visio"},

    {".wav", "audio/wav"}, {".weba", "audio/webm"}, {".webm", "video/webm"}, {".webp", "image/webp"}, {".woff", "font/woff"}, {".woff2", "font/woff2"},

    {".xhtml", "application/xhtml+xml"}, {".xls", "application/vnd.ms-excel"}, 
    {".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}, {".xml", "application/xml"}, 
    {".xul", "application/vnd.mozilla.xul+xml"}, 


    {".zip", "application/zip"}, 

    {".3gp", "video/3gpp"}, {".3g2", "video/3gpp2"}, 
    
    {".7z", "application/x-7z-compressed"}
};
